package MainPackage;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.Iterator;

import org.jgrapht.UndirectedGraph;
import org.jgrapht.alg.NeighborIndex;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.SimpleGraph;

public class GraphM {

	public UndirectedGraph<String, DefaultEdge> createStringGraph()
			throws FileNotFoundException {
		UndirectedGraph<String, DefaultEdge> g = new SimpleGraph<String, DefaultEdge>(
				DefaultEdge.class);

		File inputEdgeFile = new File(Settings.HOMEPATH + "\\"
				+ Settings.FILTEREDGE);
		Scanner edgeScanner = new Scanner(inputEdgeFile);

		int edgeCount = 0;

		HashSet<String> edgeHs = new HashSet<String>();
		int nodesInEdges = 0;

		while (edgeScanner.hasNext()) {
			String currLine = edgeScanner.next();
			// System.out.println(currLine);
			String[] currEdges = currLine.split(",");
			// System.out.println(currEdges.length);

			edgeCount++;

			if (!edgeHs.contains(currEdges[0])) {
				edgeHs.add(currEdges[0]);
				nodesInEdges++;
				g.addVertex(currEdges[0]);

			}

			if (!edgeHs.contains(currEdges[1])) {
				edgeHs.add(currEdges[1]);
				nodesInEdges++;
				g.addVertex(currEdges[1]);

			}

			g.addEdge(currEdges[0], currEdges[1]);

		}

		System.out.println("Nodes in edges count: " + nodesInEdges
				+ " Edge count: " + edgeCount);
		return g;
	}

	public void getEdgesOfNodes() throws IOException {

		FileM FileManip = new FileM(Settings.HOMEPATH);
		FileManip.filterEdgesByNodes(Settings.NODEFILE, Settings.EDGEFILE,
				Settings.FILTEREDGE);
		return;
	}

	/**
	 * @param args
	 */
	/*
	 * public static void main(String[] args) { // TODO Auto-generated method
	 * stub
	 * 
	 * }
	 */

	public Map<String, Map<String, Integer>> getTagsOfNeighborNodes(
			UndirectedGraph<String, DefaultEdge> stringGraph,
			Map<String, Map<String, Integer>> vectorMap) {

		System.out.println("getTagsOfNeighborNodes : Method Entry");

		try {
			// The Result vectorMap with tags of neighbors also.
			Map<String, Map<String, Integer>> neighborVectorMap = new HashMap<String, Map<String, Integer>>();

			// Nodes in the give graph.
			Set<String> vertices = stringGraph.vertexSet();

			// Find all the neighbors of each vertex.
			// Store all the data in a Map.

			NeighborIndex<String, DefaultEdge> index = new NeighborIndex<String, DefaultEdge>(
					stringGraph);

			Map<String, Set<String>> neighborsList = new HashMap<String, Set<String>>();

			// For each vertex
			for (String vertex : vertices) {

				// Get the nodes neighbors list.
				Set<String> neighbors = index.neighborsOf(vertex);
				neighborsList.put(vertex, neighbors);

				Map<String, Integer> tempTagMap = vectorMap.get(vertex);

				Iterator<String> iter = neighbors.iterator();

				// For each neighbor, merge the tag map of the neighbor map
				// with the vertexMap so that we get an update one.
				while (iter.hasNext()) {
					String neighborVertex = iter.next();

					// System.out.println("Node: "+vertex+", Neighbour: "+neighborVertex);

					Map<String, Integer> tagMapOfCurrNeighbor = vectorMap
							.get(neighborVertex);

					tempTagMap = mergeMap(tagMapOfCurrNeighbor, tempTagMap);

				}

				neighborVectorMap.put(vertex, tempTagMap);
				// System.out.println("Degree of vertex "+vertex+ " is " +
				// neighbors.size());
				neighborsList.put(vertex, neighbors);
			}

			System.out.println("No of nodes in updated graph: "
					+ neighborVectorMap.size());

			System.out.println("getTagsOfNeighborNodes : Method Exit");

			return neighborVectorMap;
		} catch (Exception e) {
			System.out.println("Error occured: " + e.getMessage());
			e.printStackTrace();
			return null;
		}
	}

	private Map<String, Integer> mergeMap(
			Map<String, Integer> tagMapOfCurrNeighbor,
			Map<String, Integer> tempTagMap) {
		// TODO Auto-generated method stub

		HashSet<String> allKeys = new HashSet<String>();
		HashMap<String, Integer> resultMap = new HashMap<String, Integer>();
		allKeys.addAll(tagMapOfCurrNeighbor.keySet());
		allKeys.addAll(tempTagMap.keySet());
		for (String k : allKeys) {
			int i1 = tagMapOfCurrNeighbor.containsKey(k) ? tagMapOfCurrNeighbor
					.get(k) : 0;
			int i2 = tempTagMap.containsKey(k) ? tempTagMap.get(k) : 0;
			resultMap.put(k, i1 + i2);
		}

		return resultMap;
	}

	public void generateOutputFile(
			Map<String, Map<String, Integer>> neighborVectorMap,
			String[] nodeMeta) throws IOException {
		// TODO Auto-generated method stub
		System.out.println("generateOutputFile : Method Entry");

		try {
			File oFile = new File(Settings.HOMEPATH + "\\"
					+ Settings.NEIGBORNODEDATA);
			FileWriter fileStream = new FileWriter(oFile);
			BufferedWriter out = new BufferedWriter(fileStream);

			Iterator<String> iter = neighborVectorMap.keySet().iterator();
			// For each node
			while (iter.hasNext()) {
				String key = (String) iter.next();
				Map<String, Integer> tagMap = (Map<String, Integer>) neighborVectorMap
						.get(key);

				// System.out.println("Printing each node tag map: "
				// + tagMap.toString());
				out.write(key);

				for (String metaTag : nodeMeta) {

					if (tagMap.get(metaTag) == null) {
						out.write(",0");
					} else {
						out.write("," + (Integer) tagMap.get(metaTag));

					}
				}
				out.newLine();
			}

			out.close();
		} catch (Exception e) {
			System.out.println("Error occured.");
			e.printStackTrace();
		}
	}

	public void predictLinks(UndirectedGraph<String, DefaultEdge> stringGraph,
			Map<String, Map<String, Short>> trainVectorMap, String[] nodeMeta,
			String testFile) {

		System.out.println("predictLinks: Method Entry");

		try {
			//calculateSMC(stringGraph, trainVectorMap, testFile);
			//calculateJC(stringGraph, trainVectorMap, testFile);
			calculateCommonNeigh(stringGraph, trainVectorMap, testFile);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println("predictLinks: Method Exit");
	}

	public void calculateCommonNeigh(
			UndirectedGraph<String, DefaultEdge> stringGraph,
			Map<String, Map<String, Short>> trainVectorMap, String testFile) throws IOException {
		// TODO Auto-generated method stub
		
		System.out.println("calculateCommonNeigh: Method Entry");
		
		File inputFile = new File(Settings.HOMEPATH + "\\" + testFile);
		Scanner scanner = new Scanner(inputFile);

		File oFile = new File(Settings.HOMEPATH + "\\" + "Neigh_"
				+ Settings.PREDICTEDLINKS);
		FileWriter fileStream = new FileWriter(oFile);
		BufferedWriter out = new BufferedWriter(fileStream);
		
		NeighborIndex<String, DefaultEdge> index = new
				 NeighborIndex<String, DefaultEdge>(stringGraph);
		
		Double threshold = getCommonNeighThreshold(index, Settings.TRAINFILE);
		
		while(scanner.hasNext()){
			
			String currEdge = scanner.next();
			String[] vertices = currEdge.split(",");
			
			Set<String> neighA = index.neighborsOf(vertices[0]);
			Set<String> neighB = index.neighborsOf(vertices[1]);
			Set<String> intersection = new HashSet<String>(neighA);
			
			intersection.retainAll(neighB);
						
			if((double) intersection.size() / (neighA.size() + neighB.size()) > threshold){
				out.write(vertices[0] + "," + vertices[1]);
				out.newLine();
			}
		}
		
		out.close();
		
		System.out.println("calculateCommonNeigh: Method Exit");
		
	}

	private Double getCommonNeighThreshold(
			NeighborIndex<String, DefaultEdge> index, String trainFile) throws FileNotFoundException {
		
		File inputFile = new File(Settings.HOMEPATH + "\\" + trainFile);
		Scanner scanner = new Scanner(inputFile);
		
		Double totalCommonNeigh = 0.0;
		int count = 0;
		
		while(scanner.hasNext()){
			String currEdge = scanner.next();
			String[] vertices = currEdge.split(",");
			
			Set<String> neighA = index.neighborsOf(vertices[0]);
			Set<String> neighB = index.neighborsOf(vertices[1]);
			Set<String> intersection = new HashSet<String>(neighA);
			intersection.retainAll(neighB);
			
			totalCommonNeigh+= (double) intersection.size()/((neighA.size()+neighB.size()));
			count++;

			if (count % 10000 == 0) {
				System.out.println("Calculated common neighbours for " + count
						+ " edges with Total common neigh " + totalCommonNeigh
						+ " training data so far.. still calculating.. ");
			}
		}
		
		return (double) totalCommonNeigh / count;
	}

	public void calculateJC(UndirectedGraph<String, DefaultEdge> stringGraph,
			Map<String, Map<String, Short>> trainVectorMap, String testFile)
			throws IOException {
		// TODO Auto-generated method stub

		File inputFile = new File(Settings.HOMEPATH + "\\" + testFile);
		Scanner scanner = new Scanner(inputFile);

		File oFile = new File(Settings.HOMEPATH + "\\" + "JC_"
				+ Settings.PREDICTEDLINKS);
		FileWriter fileStream = new FileWriter(oFile);
		BufferedWriter out = new BufferedWriter(fileStream);

		// Calculate a threshold value from training vectors with
		// training edges based on Jaccard coefficient.
		double threshold = getJCThreshold(stringGraph, trainVectorMap);

		int classCount = 0;
		while (scanner.hasNext()) {

			classCount++;

			String currEdge = scanner.next();
			String[] vertices = currEdge.split(",");

			// Create TagMap for Vertex A
			Map<String, Short> tagMapA = trainVectorMap.get(vertices[0]);

			// Create TagMap for Vertex B
			Map<String, Short> tagMapB = trainVectorMap.get(vertices[1]);

			if (jcSimilar(tagMapA, tagMapB) > threshold) {
				out.write(vertices[0] + "," + vertices[1]);
				out.newLine();
			}

			if (classCount % 1000 == 0) {
				System.out.println(classCount
						+ " edges classified so far. Still classifying..");
			}

		}

		out.close();
	}

	public void calculateSMC(UndirectedGraph<String, DefaultEdge> stringGraph,
			Map<String, Map<String, Short>> trainVectorMap, String testFile)
			throws IOException {
		// TODO Auto-generated method stub

		File inputFile = new File(Settings.HOMEPATH + "\\" + testFile);
		Scanner scanner = new Scanner(inputFile);

		File oFile = new File(Settings.HOMEPATH + "\\" + "SMC_"
				+ Settings.PREDICTEDLINKS);
		FileWriter fileStream = new FileWriter(oFile);
		BufferedWriter out = new BufferedWriter(fileStream);

		// Calculate a threshold value from training vectors with
		// training edges based on Simple Matching coefficient.
		double threshold = getSmcThreshold(stringGraph, trainVectorMap);

		int classCount = 0;
		while (scanner.hasNext()) {

			classCount++;

			String currEdge = scanner.next();
			String[] vertices = currEdge.split(",");

			// Create TagMap for Vertex A
			Map<String, Short> tagMapA = trainVectorMap.get(vertices[0]);

			// Create TagMap for Vertex B
			Map<String, Short> tagMapB = trainVectorMap.get(vertices[1]);

			if (smcSimilar(tagMapA, tagMapB) > threshold) {
				out.write(vertices[0] + "," + vertices[1]);
				out.newLine();
			}

			if (classCount % 100 == 0) {
				System.out.println(classCount
						+ " edges classified so far. Still classifying..");
			}

		}

		out.close();

	}

	public Double getSmcThreshold(
			UndirectedGraph<String, DefaultEdge> stringGraph,
			Map<String, Map<String, Short>> trainVectorMap) {

		// Get the train edge file.

		Set<DefaultEdge> edgeSet = stringGraph.edgeSet();

		double totalSmc = 0;
		int count = 0;

		for (DefaultEdge currEdge : edgeSet) {
			String NodeA = stringGraph.getEdgeSource(currEdge);
			String NodeB = stringGraph.getEdgeTarget(currEdge);

			totalSmc += smcSimilar(trainVectorMap.get(NodeA),
					trainVectorMap.get(NodeB));
			count++;

			if (count % 10000 == 0) {
				System.out.println("Calculated SMC for " + count
						+ " edges with Total SMC " + totalSmc
						+ " training data so far.. still calculating.. ");
			}
		}

		return (double) totalSmc / edgeSet.size();
	}

	public Double getJCThreshold(
			UndirectedGraph<String, DefaultEdge> stringGraph,
			Map<String, Map<String, Short>> trainVectorMap) {

		// Get the train edge file.

		Set<DefaultEdge> edgeSet = stringGraph.edgeSet();

		double totalJc = 0;
		int count = 0;

		for (DefaultEdge currEdge : edgeSet) {
			String NodeA = stringGraph.getEdgeSource(currEdge);
			String NodeB = stringGraph.getEdgeTarget(currEdge);

			totalJc += jcSimilar(trainVectorMap.get(NodeA),
					trainVectorMap.get(NodeB));
			count++;

			if (count % 10000 == 0) {
				System.out.println("Calculated JC for " + count
						+ " edges with Total JC " + totalJc
						+ " training data so far.. still calculating.. ");
			}
		}

		return (double) totalJc / edgeSet.size();
	}

	public Double smcSimilar(Map<String, Short> tagMapA,
			Map<String, Short> tagMapB) {

		HashSet<String> allKeys = new HashSet<String>();
		int totalTags = Settings.FEATURECOUNT;
		int existingInBoth = 0;
		int notExistingInBoth = 0;

		allKeys.addAll(tagMapB.keySet());
		allKeys.addAll(tagMapA.keySet());
		for (String k : allKeys) {
			if (tagMapB.containsKey(k) && tagMapA.containsKey(k)) {
				existingInBoth++;
			}

			if (!(tagMapB.containsKey(k) || tagMapA.containsKey(k))) {
				notExistingInBoth++;
			}
		}

		Double smc = (double) (existingInBoth + notExistingInBoth) / totalTags;

		return smc;
	}

	public Double jcSimilar(Map<String, Short> tagMapA,
			Map<String, Short> tagMapB) {

		HashSet<String> allKeys = new HashSet<String>();
		int totalTags = Settings.FEATURECOUNT;
		int existingInBoth = 0;
		int notExistingInBoth = 0;

		allKeys.addAll(tagMapB.keySet());
		allKeys.addAll(tagMapA.keySet());
		for (String k : allKeys) {
			if (tagMapB.containsKey(k) && tagMapA.containsKey(k)) {
				existingInBoth++;
			}

			if (!(tagMapB.containsKey(k) || tagMapA.containsKey(k))) {
				notExistingInBoth++;
			}
		}

		Double jc = (double) existingInBoth / (totalTags - notExistingInBoth);

		return jc;
	}

	public Map<String, Integer> getTagMap(String node, String[] nodeMeta,
			String trainFile) throws FileNotFoundException {
		// TODO Auto-generated method stub

		File inputFile = new File(Settings.HOMEPATH + "\\" + trainFile);
		Scanner scanner = new Scanner(inputFile);

		Map<String, Integer> tagMap = new HashMap<String, Integer>();
		while (scanner.hasNext()) {

			String currNode = scanner.next();

			if (!currNode.equals(node)) {
				continue;
			}

			String[] currTagVectorSplit = currNode.split(",");

			for (int k = 1; k < 27121; k++) {
				if (!currTagVectorSplit[k].equals("0")) {

					tagMap.put(nodeMeta[k - 1],
							Integer.parseInt((currTagVectorSplit[k])));

				}
			}

		}

		return tagMap;
	}

	public void junkCode() {

		/*
		 * Below code is to find for every pair of edges that are possible in
		 * graph. NeighborIndex<String, DefaultEdge> index = new
		 * NeighborIndex<String, DefaultEdge>( trainVectorMap);
		 * 
		 * // For each vertex for (String vertex : vertices) {
		 * 
		 * // Get the nodes neighbors list. Set<String> neighbors =
		 * index.neighborsOf(vertex);
		 * 
		 * Map<String, Integer> tempTagMap = nodeMeta.get(vertex);
		 * 
		 * Set<String> linkVertices = trainVectorMap.vertexSet();
		 * 
		 * for (String linkVertex : linkVertices) {
		 * 
		 * // If both nodes are same, no point to calculate similarity
		 * 
		 * if (linkVertex.equals(vertex)) { continue; }
		 * 
		 * // If we already know this linkVertex has an edge // with vertex,
		 * skip.
		 * 
		 * Iterator<String> iter = neighbors.iterator(); boolean knownNeigh =
		 * false; while (iter.hasNext()) { if (linkVertex.equals((String)
		 * iter.next())) { knownNeigh = true; } }
		 * 
		 * if (knownNeigh) { continue; }
		 * 
		 * // Calculate some similarity between two nodes to // predict a link
		 * between them.
		 * 
		 * // For each node, do an AND operation with every other node. // If
		 * that value is greater than a Threshold, mark as // predicted. // We
		 * will print such edge in a file.
		 * 
		 * Map<String, Integer> linkTagMap = nodeMeta .get(linkVertex); if
		 * (similar(tempTagMap, linkTagMap) && !linkVertex.equals(vertex)) {
		 * out.write(vertex + "," + linkVertex); out.newLine(); }
		 * 
		 * } }
		 * 
		 * out.close();
		 */
	}
}
